package com.yjzx.util.common.util.unmodifiable;

import com.yjzx.util.common.util.map.MyRefMap;
import org.springframework.cglib.proxy.*;

import java.lang.reflect.*;
import java.util.*;

/**
 * @author yjzx
 * @date 2024/8/14
 * @description FixedUtil
 */
public class FixedUtil {
    public static Object toFixed(Object o) {
        return toFixed(o, Collections.emptyList());
    }
    public static Object toFixed(Object o, List<Method> excludeMethods) {
        if (o == null) {
            return null;
        }
        if (o instanceof Collection) {
            return toFixedCollection((Collection<?>) o);
        } else if (o instanceof Map) {
            return toFixedMap((Map<?,?>) o);
        } else {
            return toFixedObj(o, excludeMethods);
        }
    }
    public static <T> T toFixedObj(T t) {
        return toPoxyObject(t, new UnmodifiableMethodInterceptor(t));
    }
    public static <T>T toFixedObj(T t,Method... excludeMethods) {
        return toFixedObj(t, Arrays.asList(excludeMethods));
    }
    public static <T>T toFixedObj(T t,List<Method> excludeMethods) {

        return toPoxyObject(t, new UnmodifiableMethodInterceptor(t, excludeMethods));
    }

    public static <T>T toFixedObj(T t,UnmodifiableObject unmodifiableObject) {
        return toPoxyObject(t, new UnmodifiableMethodInterceptor(t, unmodifiableObject));
    }

    private static <T> T toPoxyObject(T t, Callback callback) {
        if (t == null) {
            return null;
        }
        Class<?> oClass = getNoPoxyClass(t.getClass());
        if (Modifier.isFinal(oClass.getModifiers()) || t instanceof Number || t instanceof Enum) {
            return t;
        }
        Enhancer enhancer = enhancerMap.get(oClass);
        if (enhancer!=null){
            t = (T)enhancer.create();
            try {
                Field field = t.getClass().getDeclaredField("CGLIB$CALLBACK_0");
                field.setAccessible(true);
                field.set(t, callback);
                return t;
            } catch (NoSuchFieldException | IllegalAccessException ignored) {
            }
        }
        enhancer = new Enhancer();
        enhancerMap.put(oClass,enhancer);
        enhancer.setSuperclass(oClass);
        enhancer.setCallback(callback);
        return (T) enhancer.create();
    }
    private static MyRefMap<Enhancer> enhancerMap = MyRefMap.getRef(false);


    private static Class<?> getNoPoxyClass(Class<?> clazz){
        if (clazz.getName().contains("$$")){
            return getNoPoxyClass(clazz.getSuperclass());
        }
        return clazz;
    }

    public static <T> Collection<T> toFixedCollection(Collection<T> collection) {
        if (collection == null || collection.size() == 0) {
            return Collections.emptyList();
        }
        if (collection instanceof List) {
            return toFixedList((List<T>) collection);
        } else if (collection instanceof Set) {
            return toFixedSet((Set<T>) collection);
        }
        return Collections.unmodifiableCollection(collection);
    }

    public static <T> Set<T> toFixedSet(Set<T> set) {
        if (set == null || set.size() == 0) {
            return Collections.emptySet();
        } else if (set instanceof NavigableSet) {
            return Collections.unmodifiableNavigableSet((NavigableSet<T>) set);
        } else if (set instanceof SortedSet) {
            return Collections.unmodifiableSortedSet((SortedSet<T>) set);
        }
        return Collections.unmodifiableSet(set);
    }

    public static <T> List<T> toFixedList(List<T> list) {
        if (list == null || list.size() == 0) {
            return Collections.emptyList();
        } else {
            return Collections.unmodifiableList(list);
        }
    }

    public static <K, V> Map<K, V> toFixedMap(Map<K, V> map) {
        if (map == null || map.size() == 0) {
            return Collections.emptyMap();
        } else if (map instanceof NavigableMap) {
            return Collections.unmodifiableNavigableMap((NavigableMap<K, V>) map);
        } else if (map instanceof SortedMap) {
            return Collections.unmodifiableSortedMap((SortedMap<K, V>) map);
        }
        return Collections.unmodifiableMap(map);
    }
}
